<!DOCTYPE html>
<head>
    <meta charset="utf-8">

    <script src="/vendor/d3/d3-3.3.4.js"></script>
    <script src="/vendor/dagre-d3/dagre-d3-0.3.2.js"></script>
    <script src="/units.js"></script>

    <style>
        body {
            background: #333;
            position: fixed;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            margin: 0;
            padding: 0;
        }

        #graph {
            width: 100%;
            height: 100%;
        }

        #graph text {
            font-size: 14px;
        }

        .node rect {
            stroke-width: 1.5px;
            stroke: #bbb;
        }

        .task {
            color: #fff;
            width: 330px;
        }

        .taskid {
            font-size: 20px;
        }

        .output {
            border-bottom: 1px dashed #bbb;
            margin-bottom: 5px;
            padding: 10px;
            background: #808080;
        }

        .output.slow {
            background: #f66;
        }

        .input {
            border-top: 1px dashed #bbb;
            margin-top: 5px;
            padding: 10px;
            background: #808080;
        }

        .task-details {
            padding: 10px;
        }

        .node.state-PLANNED > rect {
            fill: #5d5d5d;
        }

        .node.state-RUNNING > rect {
            fill: #46c13f;
        }

        .node.state-FINISHED > rect {
            fill: #427daa;
        }

        .node.state-FAILED > rect {
            fill: rgb(255, 60, 65);
        }

        .node.state-CANCELED > rect {
            fill: #d9d952;
        }

        .edgePath path {
            stroke: #999;
            stroke-width: 1.5px;
            fill: #999;
        }
    </style>
</head>

<body>

<svg id="graph">
    <g></g>
</svg>

</body>

<script type="text/javascript">

var svg = d3.select("#graph");
var g = svg.select("g");
var render = new dagreD3.render();
var refresh = true;
var first = true;

var zoom = d3.behavior.zoom().on("zoom", function () {
    g.attr("transform", "translate(" + d3.event.translate + ")" + "scale(" + d3.event.scale + ")");
});

svg.call(zoom);

function redraw() {
    d3.json('/v1/query-execution/' + window.location.search.substring(1), function (error, json) {
        if (error && !refresh) {
            setTimeout(redraw, 1000);
            return;
        }

        var graph = new dagreD3.graphlib.Graph();
        graph.setGraph({
            rankdir: "BT",
            nodesep: 70,
            ranksep: 200,
            marginx: 50,
            marginy: 50
        });

        var tasks = json.tasks;
        var flows = json.flows;

        tasks.forEach(function (task) {
            var html = "<div class='task'>";
            html += "<div class='output " + (task.bufferedPages > 0 ? "slow" : "") + "'>" + formatCount(task.outputRows) + " rows (" + formatDataSize(task.outputBytes) + "), buffered pages: " + task.bufferedPages + "</div>";
            html += "<div class='task-details'>";
            html += "<div class='taskid'>" + task.taskId + "</div>";
            html += "<div class='host'>" + task.host + "</div>";
            html += "<span class='elapsedTime'>Elapsed: " + formatDuration(task.uptime) + "</span> / ";
            html += "<span class='cpuTime'>CPU: " + formatDuration(task.cpuMillis) + "</span> / ";
            html += "<span class='blockedTime'>Blocked: " + formatDuration(task.blockedMillis) + "</span>";
            html += "<div class='memory'>Memory: " + formatDataSize(task.usedMemoryBytes) + "</div>";
            html += "<div>Splits: " + task.queuedSplits + " queued, " + task.runningSplits + " running, " + task.completedSplits + " completed</div>";
            html += "</div>"; // task details
            html += "<div class='input'>" + formatCount(task.inputRows) + " rows (" + formatDataSize(task.inputBytes) + ")" + "</div>";
            html += "</div>";

            graph.setNode(task.taskId, {
                labelType: "html",
                label: html,
                class: "state-" + task.state + (task.bufferedPages > 0 ? " inactive" : ""),
                padding: 0
            });
        });

        flows.forEach(function (flow) {
            var color;

            if (flow.bufferedPages > 0) {
                color = "#f66";
            }
            else if (flow.finished) {
                color = "#999";
            }
            else {
                color = "#46c13f";
            }

            graph.setEdge(flow.from, flow.to, {
                label: "",
                lineInterpolate: "basis",
                style: "stroke: " + color + "; fill: none;",
                arrowheadStyle: "stroke: " + color + "; fill: " + color + ";"
            });
        });

        g.call(render, graph);

        if (first) {
            // Zoom and scale to fit
            var width = graph.graph().width + 80;
            var height = graph.graph().height + 40;

            var targetWidth = parseInt(svg.style("width").replace(/px/, ""));
            var targetHeight = parseInt(svg.style("height").replace(/px/, ""));

            var scale = Math.min(targetWidth / width, targetHeight / height);
            var position = [(targetWidth / 2) - ((width * scale) / 2), (targetHeight / 2) - ((height * scale) / 2)];
            zoom.translate(position);
            zoom.scale(scale);

            zoom.event(svg);
        }
        first = false;

        setTimeout(redraw, 1000);
    });
}

redraw();

</script>
